package test;

import java.applet.Applet;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.*;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.LinkedList;

import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;



import com.pnemec.addlog.LogModule;
import com.pnemec.addlog.LogXmlReader;
import com.pnemec.net.Uploader;
import com.pnemec.swing.*;
import com.pnemec.util.DataProvider;
import com.pnemec.util.Debug;

//import modules.TestingLogModule;

/**
 * Apple application for gathering logs into tar file and uploading them to server
 * params
 *  DEBUG="true"  - true/false - open debuging window to see inner behavior 
 *	UPLOAD_URL="http://wintermute.suse.cz/~pavel/__upload" - upload target for POST event
 *  MODULES="data/xorg.xml;data/java.xml" - ';' separated list of modules. Every module will be offered to user as possibility. Modules are packed in jar file
 * @author Pavel Nemec <pnemec@suse.cz>
 * 
 */
public class MyApplet extends Applet implements ActionListener {
	public static int SIZE = 12;

	/* how many logs gather will run at same time */
	public static int CONCURRENT_RUNNING_LOGS = 1;
	
	/* argument parametr, if true all logs are selected by default */
	private boolean PRESELECTED=false;


	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private LinkedList logModuleList = null;

	private LinkedList selectedML = null;

	private Gatherer gatherer = null;

	/** GUI variables * */

	// private int needle=0;
	private JButton gatherJB = null;

	private JRadioButton uploadJRB = null, saveTarJRB = null, textJRB = null;
	
	private JButton helpJB;
	
	/* after click on run we need to disable all clickable components, lets store theme here */
	private LinkedList disableList=null;
	
	/* store all text able components to set font size and so on*/
	private LinkedList textComponents=null;

	/* we know this value just after start, we can use it to init gui */
	private int moduleCount = -1;

	/* should be past from html page, disable upload button if is null*/
	private String urlForUpload=null;


	public void init() {
		this.setBackground(Color.gray);
		String preselected, moduleList,debug,size;
		preselected		= this.getParameter("PRESELECTED");
		moduleList 		= this.getParameter("MODULES");
		size			= this.getParameter("SIZE");
		debug			= this.getParameter("DEBUG");
		urlForUpload = this.getParameter("UPLOAD_URL");
		
		
		if (preselected != null && preselected.compareTo("true")==0	 )
			this.PRESELECTED=true;
		
		if (moduleList != null){
//			String[] parts=m");
			initModules(moduleList.split("[,;]"));
		}
		else
			initModules(null);
		
		if (size != null){
			try{
				MyApplet.SIZE = Integer.parseInt(size);
			}
			catch (NumberFormatException nfe){
				MyApplet.SIZE=12;//just default
			}
		}
		
		if(debug!=null && debug.compareTo("true")==0){
			com.pnemec.util.Debug.enableDebug();
			com.pnemec.util.Debug.showDebugWindow(this);			
		}
			
		initGui();

		Debug.log("Applet.init() finish, PRESELECTED: "+this.PRESELECTED+", logs: "+moduleList+", size: "+size+", debug: "+debug);
	}

	/**
	 * init all modules passed from html page TODO need to be threded !
	 * 
	 * @param modules
	 */
	private void initModules(String[] modules) {
		logModuleList = new LinkedList();
		selectedML = new LinkedList();
		if (modules != null) {
			for (int i = 0; i < modules.length; i++) {
				String name=modules[i];
				name = name.replaceAll("^ ", "");
				name = name.replaceAll(" $", "");
				Debug.log("going to read module with name:>"+name+"<");
				/**
				 * TODO - if initialization fail, I should not add this module
				 */
				logModuleList.add(new LogXmlReader(modules[i]));
			}
			if(this.PRESELECTED==true)
				selectedML.addAll(logModuleList);
		}

	}

	private void initGui() {
		textComponents=new LinkedList();
		disableList = new LinkedList();
		
		this.setLayout(new BorderLayout());

		// controlls for work flow
		gatherJB = new JButton("Gather LOGs");
		JPanel controlJP = new JPanel(new FlowLayout(FlowLayout.CENTER));
		gatherJB.addActionListener(this);
		controlJP.add(gatherJB);
		gatherJB.setEnabled(selectedML.size()>0);
		
		helpJB = new JButton();
		Image i = DataProvider.getImage("help.png",this.SIZE);
		if(i!=null)
			helpJB.setIcon(new ImageIcon(i));
		else
			helpJB.setText("H");
		helpJB.setToolTipText("Click to show Help");
//		controlJP.add(helpJB);

		JLabel label = new JLabel("Welcome to Bugzilla log collector");
		JPanel headerJP = new JPanel(new FlowLayout(FlowLayout.CENTER));
		headerJP.add(label);
		
		textComponents.add(gatherJB);
		textComponents.add(helpJB);
		textComponents.add(label);

		

		// panel with module list (could be long)
		JPanel mainJP =initMainJP();
		JScrollPane scrollJSP= new JScrollPane(mainJP);
		
		this.add(BorderLayout.CENTER, scrollJSP);
		
		// panel with result selection
		this.add(BorderLayout.NORTH, initResultJP());
		
		this.add(BorderLayout.SOUTH, controlJP);

		DataProvider.setParrentComponent(this);
		

	}

	private JPanel initMainJP() {

		JPanel mainJP  = new JPanel();
		JPanel centerJP = new JPanel(new BorderLayout(5, 5));

		JPanel left, center, right;
		left = new JPanel(new GridLayout(moduleCount, 1, 5, 5));
		right = new JPanel(new GridLayout(moduleCount, 2, 5, 5));
		center = new JPanel(new GridLayout(moduleCount, 1, 5, 5));
		
		for (Iterator iter = logModuleList.iterator(); iter.hasNext();) {
			LogXmlReader element = (LogXmlReader) iter.next();
			
			/* checkbox to select/unselect module */
			LogModuleJCB elementJCB = new LogModuleJCB(element);
			elementJCB.setText("");
			elementJCB.setToolTipText(element.getDescription());
			elementJCB.setHorizontalTextPosition(JRadioButton.LEFT);
			elementJCB.addItemListener(new ItemListener() {
				public void itemStateChanged(ItemEvent arg0) {
					LogModuleJCB box = (LogModuleJCB) arg0.getSource();
					if (box.isSelected()) {
						selectedML.add(box.getModule());
						gatherJB.setEnabled(true);
					} else {
						selectedML.remove(box.getModule());
						if (selectedML.size() == 0)
							gatherJB.setEnabled(false);
					}
				}
			});
			
			/* button with icon, it is able to show some information as pop up */
			elementJCB.setSelected(selectedML.contains(element));
			InfoJB ijb = new InfoJB(element);
			ijb.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					LogModule m = ((InfoJB) e.getSource()).getModule();
					DataProvider.showMessage(m.getName(), m.getDescription());
				}
			});
			
			/* name, during execution it will be changed to status info */
			JLabel nameJL = new JLabel(element.getName());
			nameJL.setToolTipText(element.getName());
			
			element.addEventListener(new StatusListener(nameJL));

			/* animated circular progress bar */
			AnimatedProgressBar apb = new AnimatedProgressBar(DataProvider
					.getIconSize());
			element.addEventListener(apb);



			center.add(nameJL);
			left.add(ijb);
			right.add(elementJCB);
			right.add(apb);
			
			String tooltip = element.getSummary();
			ijb.setToolTipText(tooltip);
			elementJCB.setToolTipText(tooltip);
			nameJL.setToolTipText(tooltip);
			
			textComponents.add(nameJL);
			textComponents.add(apb);
			textComponents.add(ijb);
			
			disableList.add(elementJCB);
		}
		centerJP.add(BorderLayout.WEST, left);
		centerJP.add(BorderLayout.CENTER, center);
		centerJP.add(BorderLayout.EAST, right);
		mainJP.add(centerJP);
		return mainJP;
	}

	private JPanel initResultJP() {
		// 
		JPanel main = new JPanel();
				
		
		uploadJRB = new JRadioButton("Upload", true);
		uploadJRB
				.setToolTipText("all log will be packed in one big tar.bz2 archive and uploaded to server");
		saveTarJRB = new JRadioButton("Save", false);
		saveTarJRB
				.setToolTipText("all logs will bge packed to archive and save to user Desktop");
		textJRB = new JRadioButton("Show", false);
		textJRB
				.setToolTipText("all logs will be displayed in special window for copy&paste");
		ButtonGroup buttonGroup = new ButtonGroup();

		uploadJRB.addActionListener(this);
		saveTarJRB.addActionListener(this);
		textJRB.addActionListener(this);
		buttonGroup.add(uploadJRB);
		buttonGroup.add(saveTarJRB);
		buttonGroup.add(textJRB);
		main.add(uploadJRB);
		main.add(saveTarJRB);
		main.add(textJRB);
		disableList.add(uploadJRB);
		disableList.add(saveTarJRB);
		disableList.add(textJRB);
		return main;
	}
	
	private void disableComponents(){
		for (Iterator iterator = disableList.iterator(); iterator.hasNext();) {
			((Component) iterator.next()).setEnabled(false);
			
		}
	}
	/**
	 * implementation of Gather main button
	 * - showing result (uploading, copying showing is done here)
	 */
	public void actionPerformed(ActionEvent arg0) {
		
		Object source = arg0.getSource();
		if (source == gatherJB) {
			Debug.log("user click on main button");
			if (gatherer == null) {
				int type = DataProvider.getType();
				for (Iterator iterator = selectedML.iterator(); iterator.hasNext();) {
					LogModule module = (LogModule) iterator.next();
					module.setType(type);
				}
				Debug.log("user click on start");
				disableComponents();
				gatherer = new Gatherer();
				gatherer.selfStart();
				return;
			}
			if (gatherer.isRunning) {
				Debug.log("user click on Cancel ");
				gatherJB.setText("restart");
				gatherer.setShouldRun(false);
				gatherer=null;
				return;
			}
			if (!gatherer.isRunning) {
				Debug.log("user click on finish");
				
				/* here is result handling implementation */
				if (saveTarJRB.isSelected()) {
					/*
					 * save all tars from temporary location to user selected
					 * location
					 */
					File location = null;
					JFileChooser jfc = new JFileChooser();
					jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
					int reti = jfc.showDialog(this, "Save result to location");
					if (reti == JFileChooser.APPROVE_OPTION) {
						location = jfc.getSelectedFile();
						LinkedList result = DataProvider.packTars();
						if (result != null) {
							for (Iterator iterator = result.iterator(); iterator
									.hasNext();) {
								File file = (File) iterator.next();
							
								FileChannel ic = null;
								FileChannel oc = null;
								if (file.exists()) {
									try {
										ic = new FileInputStream(file)
												.getChannel();
										oc = new FileOutputStream(location
												+ "/" + file.getName())
												.getChannel();
										ic.transferTo(0, ic.size(), oc);
										ic.close();
										oc.close();
									} catch (FileNotFoundException fnfe) {
										Debug
												.log("MyApplet, copyying result, FileNotfoundException"
														+ fnfe.getMessage());
									} catch (IOException ioe) {

									} finally {
										if (ic.isOpen()) {
											try {
												ic.close();
											} catch (IOException e) {
												System.out
														.println("second IO Exception in MyApplet when copying files");
											}
										}
										if (oc.isOpen()) {
											try {
												oc.close();
											} catch (IOException e) {
												System.out
														.println("second IO Exception in MyApplet when copying files");
											}
										}
									}
								}
							}
						}
					}
				}
				if(textJRB.isSelected()){
					//create new window and put it into scrollable text area
					JTextArea jta = new JTextArea();
//					LinkedList selectedML 
					for (Iterator iterator = selectedML.iterator(); iterator
							.hasNext();) {
						LogModule type = (LogModule) iterator.next();
						jta.append("Result for: "+type.getName()+"\n");
						try {
							jta.append(type.getTextResult());
						} catch (Exception e) {
							jta.append("Error during retriving text result");
							Debug.log("MyApplet: problem during retriving text result: "+e.getMessage());
						}
						jta.append("**************************************************");
					}
					JPanel jtaJP = new JPanel(new BorderLayout());
					
//					jta.setPreferredSize(new Dimension(640,480));
					
					JScrollPane scrollPane = new JScrollPane(jta);
					
					jtaJP.add(scrollPane,BorderLayout.CENTER);
					
					/* show the result using modal dialog */
					JDialog dialog = new JDialog();
					dialog.add(jtaJP);
					dialog.setPreferredSize(new Dimension(320,240));
					dialog.setModal(true);
					dialog.pack();
					/* set bars to start */
					scrollPane.getHorizontalScrollBar().setValue(0);
					scrollPane.getVerticalScrollBar().setValue(0);
					dialog.setVisible(true);
				}
				if(uploadJRB.isSelected()){
					Debug.log("executing upload querry");
					URL url;
					try {
						url = new URL(urlForUpload);
						Uploader u = new Uploader(url, DataProvider.getGlobalTarName());
						Debug.log("MyApplet.actionPerformed: going to upload files");
						u.doUpload();
						if(u.getStatus()==Uploader.FINISHED_OK){
							Debug.log("MyApplet.actionPerformed: upload finished succesfully");
						}
						else{
							Debug.log("MyApplet.actionPerformed: upload failed: "+u.getReturnMessage());
						}
						
					} catch (MalformedURLException e) {
						Debug.log("MyApplet.actionPerformed: applet get wrong URL to upload to");
						e.printStackTrace();
					}catch (FileNotFoundException fnfe){
						Debug.log("MyApplet.actionPerformed: applet tar file can`t f");
					}
					
				}
			}
		}
		if (source == saveTarJRB || source == uploadJRB) {
			Debug.log("setting file type from main: ");
			DataProvider.setType(LogModule.TYPE_ALL_ZIPPED_UPLOAD);
		}
		if (source == textJRB) {
			Debug.log("setting text type from main: ");
			DataProvider.setType(LogModule.TYPE_TEXT_SAVE_AS_FILE);
			
		}
	}

	public static void main(String[] argc) {
		JFrame f = new JFrame("Running as Application");
		MyApplet ma = new MyApplet();
		f.getContentPane().add(ma);
		ma.init();
		f.setSize(640, 480);
		f.setLocation(50, 50);
		f.setVisible(true);
	}

	/**
	 * Listen for events and update JLabels // *
	 * 
	 * @author Pavel Nemec <pnemec@suse.cz> (c)2008
	 * 
	 */
	class StatusListener implements SimpleEventListener {
		JLabel label = null;

		public StatusListener(JLabel target) {
			this.label = target;
		}

		/**
		 * past status to target
		 */
		public void notifyEvent(SimpleEventAdapter s) {
			label.setText(((LogModule) s).getStatus());
		}

	}

	/**
	 * this class implement gathering cycle, going trough all selected Log Modules and 
	 * run them. Not all at once see CONCURRENT_RUNNING_LOGS
	 * @author Pavel Nemec <pnemec@suse.cz>
	 *
	 */
	class Gatherer implements Runnable {
		private Thread t = null;

		private boolean shouldRun = true;
		private boolean isRunning = false;

		public void run() {
			isRunning = true;
			gatherJB.setText("Cancel");
			gatherJB.revalidate();
			LinkedList toRunList = new LinkedList(); // all tasks
			LinkedList runList = new LinkedList(); // currently running tasks
			LinkedList finishedList = new LinkedList(); // finished tasks
			LinkedList tmpList = new LinkedList(); // temporary list for
													// finished items

			toRunList.addAll(selectedML);
			/* start all/couple tasks and monitor output. Current taskas are
			 stored in runList */
			for (int i = 0; i < CONCURRENT_RUNNING_LOGS && toRunList.size() > 0; i++) {
				LogModule item = (LogModule) toRunList.removeFirst();
				runList.add(item);
				try {
					item.startGatherLogs();
					Debug.log("scheduled item: " + item.getName()
							+ " to exetuion");
				} catch (Exception e) {
					System.out.println("Gatherer.run: can start thread:"
							+ (item == null ? "null" : item.getName()));
					e.printStackTrace();
				}
			}
			/* main loop, put some task to current list and whenever one is finished, execute another */
			while (shouldRun && runList.size() != 0) {

				for (Iterator iterator = runList.iterator(); iterator.hasNext();) {
					LogModule item = (LogModule) iterator.next();
					Debug.log(item.getName() + "status of item("
							+ runList.indexOf(item) + ") " + item.getStatus());
					if (item.isRunning() == false) {
						Debug.log("item(" + runList.indexOf(item) + ") "
								+ item.getName() + " finished");
						tmpList.add(item);
					}
				}
				if (tmpList.size() > 0) {
					Debug.log("removing " + tmpList.size()
							+ " finished tasks from scheduler("
							+ runList.size() + ")");
					runList.removeAll(tmpList);
					finishedList.addAll(tmpList);
					tmpList.clear();
					/* we remove some task, so we should have space for pending ones*/
					for (int i = 0; i < CONCURRENT_RUNNING_LOGS && toRunList.size() > 0; i++) {
						LogModule item = (LogModule) toRunList.removeFirst();
						runList.add(item);
						try {
							item.startGatherLogs();
							Debug.log("scheduled item: " + item.getName()
									+ " to exetuion");
						} catch (Exception e) {
							System.out.println("Gatherer.run: can start thread:"
									+ (item == null ? "null" : item.getName()));
							e.printStackTrace();
						}
					}
					Debug.log("removed finish, still " + toRunList.size()
							+ " to go");
				}
				

				if (runList.size() != 0) {
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						// no need to catch anything
						Debug.log("interupting Gatherer from sleep");
					}
				}
			}
//			Debug.log("Gatherer main loop  end. shouldRun: "+shouldRun+", runList size: "+runList.size());
			if (toRunList.size() != 0) {
				Debug.log("Gatherer.run: I was stopped! " + toRunList.size()
						+ "stopping unfinished tasks");
				for (Iterator iterator = toRunList.iterator(); iterator
						.hasNext();) {
					((LogModule) iterator.next()).stopGatherLogs();
				}
			}
			if (toRunList.size() == 0) {
				Debug.log("gatherer.run: finished " + finishedList.size()
						+ " jobs");
				gatherJB.setText("Finish");
				gatherJB.revalidate();
			}
			isRunning = false;
		}

		public void selfStart() {
			if (t == null) {
				t = new Thread(this);
				t.start();
			}

		}

		public boolean isRunning() {
			return isRunning;
		}

		public boolean isShouldRun() {
			return shouldRun;
		}

		public void setShouldRun(boolean shouldRun) {
			this.shouldRun = shouldRun;
			if (!shouldRun) {
				Debug.log("Gatherer.setShouldRun to false, stopping");
			}
		}
	}
}
